#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <UIKit/UIApplication.h>
#import <LockInfo/Plugin.h>
#import <EventKit/EKEvent.h>

#import "framework/FWCommon.h"
#import "framework/FWApplication.h"
#import "framework/FWPreferences.h"

#import "PICommon.h"
#import "PIIntegration.h"
#import "PICalPlugin.h"

#import "PIEventNative.h"
#import "PIEvent.h"
#import "PIAlarm.h"
#import "PIRecurrance.h"
#import "PIAttendee.h"


@implementation PIEventNative


// ******************************************************************************************
#pragma mark -
#pragma mark Fetch a list of basic events

+ (NSArray*)basicEventsList:(FWApplication*)application prefs:(FWPreferences*)prefs fromDate:(NSDate*)startDate toDate:(NSDate*)endDate
{
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    NSDate* now = [NSDate date];
    
    EKEventStore* eventStore = ((PICalPlugin*)application.fwPlugin).eventStore;
    NSArray* hiddenCals = [prefs arrayValue:kPIOptionsHiddenNativeCalendars withDefault:[NSArray array]];
    NSMutableArray* calendars = [NSMutableArray arrayWithCapacity:eventStore.calendars.count];
    for (EKCalendar* calendar in eventStore.calendars) {
        if ([hiddenCals indexOfObject:calendar.title] == NSNotFound)
            [calendars addObject:calendar];
    }
    
    NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:calendars];
    
    // Fetch all events that match the predicate.
    NSArray* events = [eventStore eventsMatchingPredicate:predicate];
    NSMutableArray* results = [NSMutableArray arrayWithCapacity:[events count]];
    
    for (EKEvent* event in events) {
        PIEvent* basic = [PIEvent nativeEventWithKey:event.eventIdentifier
                                               title:event.title ? event.title : localize(bundle, @"Event/New Event")
                                                icon:nil
                                            location:event.location
                                           startDate:event.startDate
                                             endDate:event.endDate
                                              allDay:event.isAllDay
                                            calendar:event.calendar.title
                                        calendarIcon:nil
                                       calendarColor:[UIColor colorWithCGColor:event.calendar.CGColor]];

        switch (event.availability) {
            case EKEventAvailabilityBusy:
                basic.availability = kPICalAvailabilityBusy;
                break;
            case EKEventAvailabilityFree:
                basic.availability = kPICalAvailabilityFree;
                break;
            case EKEventAvailabilityTentative:
                basic.availability = kPICalAvailabilityTentative;
                break;
            case EKEventAvailabilityUnavailable:
                if (event.calendar.type == EKCalendarTypeExchange)
                    basic.availability = kPICalAvailabilityOutOfOffice;
                else
                    basic.availability = kPICalAvailabilityUnavailable;
                break;
            default:
                basic.availability = kPICalAvailabilityNotSupported;
                break;
        }

        switch (event.status) {
            case EKEventStatusConfirmed:
                basic.status = PIEventStatusTypeConfirmed;
                break;
            case EKEventStatusTentative:
                basic.status = PIEventStatusTypeTentative;
                break;
            case EKEventStatusCanceled:
                basic.status = PIEventStatusTypeCancelled;
                break;
            default:
                basic.status = PIEventStatusTypeNone;
                break;
        }

        if ([event respondsToSelector:@selector(isInvite)]) {
            basic.invitation = [event isInvite] != nil;
            
            // Is invitation still outstanding?
            if (basic.invitation && [event respondsToSelector:@selector(isInvite)])
                basic.invitation = (int)[event participationStatus] == 1;
        }

        basic.hasAttendees = event.attendees.count > 0;
        basic.repeating = event.recurrenceRule != nil;
        
        if (event.alarms && ([event.alarms count] > 0)) {
            NSDate* firstAlarm = nil;
            NSDate* nextAlarm = nil;
            
            for (EKAlarm* alarm in event.alarms) {
                NSDate* testAlarm = alarm.absoluteDate ? alarm.absoluteDate : [event.startDate addTimeInterval:alarm.relativeOffset];
                
                if (!firstAlarm || [firstAlarm isLaterThanDate:testAlarm])
                    firstAlarm = testAlarm;
                
                if ([testAlarm isLaterThanDate:now]) {
                    if (!nextAlarm || [nextAlarm isLaterThanDate:testAlarm])
                        nextAlarm = testAlarm;
                }
            }
            
            if (firstAlarm)
                basic.firstAlarm = firstAlarm;
            if (nextAlarm)
                basic.nextAlarm = nextAlarm;
        }
        
        [results addObject:basic];
    }
    
    return results;
}


+ (PIEvent*)augmentedBasicEvent:(PIEvent*)source application:(FWApplication*)application
{
    PIEvent* augment = [[source copy] autorelease];
    augment.augmented = YES;

    EKEventStore* eventStore = ((PICalPlugin*)application.fwPlugin).eventStore;

    EKEvent* event = [eventStore eventWithIdentifier:augment.key];
    
    if (event.organizer)
        augment.organiser = event.organizer.name;
    
    NSMutableArray* attendees = [NSMutableArray arrayWithCapacity:10];
    for (EKParticipant* attendee in event.attendees) {
        kPIParticipantStatusType status = kPIParticipantStatusUnknown;
        switch (attendee.participantStatus) {
            case EKParticipantStatusPending:
                status = kPIParticipantStatusPending;
                break;
            case EKParticipantStatusAccepted:
                status = kPIParticipantStatusAccepted;
                break;
            case EKParticipantStatusDeclined:
                status = kPIParticipantStatusDeclined;
                break;
            case EKParticipantStatusTentative:
                status = kPIParticipantStatusTentative;
                break;
            case EKParticipantStatusDelegated:
                status = kPIParticipantStatusDelegated;
                break;
            case EKParticipantStatusCompleted:
                status = kPIParticipantStatusCompleted;
                break;
            case EKParticipantStatusInProcess:
                status = kPIParticipantStatusInProgress;
                break;
            default:
                status = kPIParticipantStatusUnknown;
                break;
        }

        NSString* name = attendee.name;
        if (!name)
            name = [attendee.URL absoluteString];
        if ([name hasPrefix:@"mailto:"])
            name = [name substringFromIndex:7];

        [attendees addObject:
         [PIAttendee attendeeWithName:name status:status]];
    }
    
    if (attendees.count > 0) {
        [attendees sortUsingFunction:compareAttendees context:nil];
        augment.attendees = attendees;
        augment.hasAttendees = YES;
    }
    
    if (event.alarms && ([event.alarms count] > 0)) {
        NSMutableArray* alarms = [NSMutableArray arrayWithCapacity:event.alarms.count];
        for (EKAlarm* alarm in event.alarms) {
            PIAlarm* newAlarm = alarm.absoluteDate ?
              [PIAlarm alarmWithKey:@"" date:alarm.absoluteDate type:kPICalAlarmTypeNative] :
              [PIAlarm alarmWithKey:@"" relativeDate:alarm.relativeOffset baseDate:event.startDate type:kPICalAlarmTypeNative];
            [alarms addObject:newAlarm];
        }
        augment.alarms = alarms;
    }
    
    if (augment.isRepeating)
        augment.recurrance = [PIRecurrance recurranceWithRule:event.recurrenceRule];

    if (event.notes.length > 0)
        augment.notes = event.notes;
    
#ifdef DEVELOPMENT_MODE
    augment.notes = @"\n\n"
    "Here are notes that should have to be truncated.\n"
    "They are much too long to fit within the notes section as they are\n"
    "They also have two blank lines at the start which should be removed\n"
    "\n\n"
    "This is another line that ought to be bulking them out\n"
    "And here is another line that ought to be bulking them out\n"
    "Yet another line that ought to be bulking them out\n"
    "And finally another line that ought to be bulking them out\n"
    "There is also a phone number in this line - call 0800 234567 immediately!\n";
#endif
    
    return augment;
}

@end
